#pragma once

#include "comm/znx_string.h"
#include "comm/znx_queue.h"
#include "comm/znx_pool.h"
#include "comm/znx_crc.h"

#define ZNX_HTTP_HEADER_HASH_SLOT       32


typedef struct {
    uint32_t                    crc;
    u_char                      *data;
    size_t                      len;
} znx_http_header_name_t;


typedef int(*znx_http_header_cmp_handle)(znx_http_header_name_t *a, znx_http_header_name_t *b);


static inline void
znx_http_header_name_init(znx_http_header_name_t *name,
    u_char *data, size_t len)
{
    name->crc = znx_crc32_long_lower(data, len);
    name->data = data;
    name->len = len;
}


static inline int
znx_http_header_casecmp(znx_http_header_name_t *a, znx_http_header_name_t *b)
{
    if (a->crc > b->crc) {
        return 1;
    }

    if (a->crc < b->crc) {
        return -1;
    }

    if (a->len > b->len) {
        return 1;
    }

    if (a->len < b->len) {
        return -1;
    }

    return znx_strncasecmp(a->data, b->data, a->len);
}


typedef struct {
    znx_http_header_name_t      name;
    znx_str_t                   val;
    znx_queue_t                 queue;
    znx_queue_t                 queue_in_hash;
} znx_http_header_t;


typedef struct {
    znx_queue_t                 head;
    znx_queue_t                 hash_lists[ZNX_HTTP_HEADER_HASH_SLOT];
    znx_queue_t                 free_head;
    znx_http_header_cmp_handle  cmp_handle;
} znx_http_headers_t;


static inline void
znx_http_headers_init(znx_http_headers_t *headers)
{
    znx_queue_init(&headers->head);
    for (int i = 0; i < ZNX_HTTP_HEADER_HASH_SLOT; i++) {
        znx_queue_init(&headers->hash_lists[i]);
    }
    znx_queue_init(&headers->free_head);
    headers->cmp_handle = znx_http_header_casecmp;
}


// add the header to the tail of the queue.
void znx_http_headers_add(znx_http_headers_t *headers, znx_http_header_t *header);


// use the header to cover all same headers.
// if no header with the same name exits, will add the header to the tailf of the queue.
void znx_http_headers_set(znx_http_headers_t *headers, znx_http_header_t *header);


// get the i-th header with the same name.
// to get the first header, just pass in 0.
znx_http_header_t *znx_http_headers_get(znx_http_headers_t *headers,
    znx_http_header_name_t *name, size_t index);


// delete the first matching header.
void znx_http_headers_del_once(znx_http_headers_t *headers,
    znx_http_header_name_t *name);


// delete all matching headers.
void znx_http_headers_del(znx_http_headers_t *headers,
    znx_http_header_name_t *name);


znx_http_header_t *
znx_http_headers_get_free_hader(znx_http_headers_t *headers);
